using System;
using System.Configuration;
using System.IO;
using System.Collections.Generic;

using log4net;
using log4net.Config;
using System.Text;
using log4net.Util;
using System.Reflection;

namespace Wanet.PublicPortal.Common.Utilities
{
	/// <summary>
	/// Utility class for getting the logging configuration and configured
	/// logger object.
	/// </summary>
	public class Logging
	{
		#region INNER CLASSES

		/// <summary>
		/// The wrapper for log4net ILog logger instance.
		/// </summary>
		public class LoggerWrapper
		{
			#region PRIVATE FIELDS

			private int _deepIndex = 0;
			private string _deepSpace = "    ";
			private ILog _log = null;

			#endregion // PRIVATE FIELDS

			#region CONSTRUCTOR, DISPOSE

			internal LoggerWrapper(ILog log)
			{
				_log = log;
			}

			#endregion // CONSTRUCTOR, DISPOSE

			#region PUBLIC PROPERTIES

			/// <summary>
			/// Gets a value indicating whether the debug level is enabled.
			/// </summary>
			/// <value>
			/// 	<c>true</c> if the debug level is enabled; otherwise, <c>false</c>.
			/// </value>
			public bool IsDebugEnabled
			{
				get
				{
					return _log.IsDebugEnabled;
				}
			}

			/// <summary>
			/// Gets a value indicating whether the error level is enabled.
			/// </summary>
			/// <value>
			/// 	<c>true</c> if the error level is enabled; otherwise, <c>false</c>.
			/// </value>
			public bool IsErrorEnabled
			{
				get
				{
					return _log.IsErrorEnabled;
				}
			}

			/// <summary>
			/// Gets a value indicating whether the fatal level is enabled.
			/// </summary>
			/// <value>
			/// 	<c>true</c> if the fatal level is enabled; otherwise, <c>false</c>.
			/// </value>
			public bool IsFatalEnabled
			{
				get
				{
					return _log.IsFatalEnabled;
				}
			}

			/// <summary>
			/// Gets a value indicating whether the info level is enabled.
			/// </summary>
			/// <value>
			/// 	<c>true</c> if the info level is enabled; otherwise, <c>false</c>.
			/// </value>
			public bool IsInfoEnabled
			{
				get
				{
					return _log.IsInfoEnabled;
				}
			}

			/// <summary>
			/// Gets a value indicating whether the warn level is enabled.
			/// </summary>
			/// <value>
			/// 	<c>true</c> if the warn level is enabled; otherwise, <c>false</c>.
			/// </value>
			public bool IsWarnEnabled
			{
				get
				{
					return _log.IsWarnEnabled;
				}
			}

			/// <summary>
			/// Gets the logger.
			/// </summary>
			/// <value>The logger.</value>
			public log4net.Core.ILogger Logger
			{
				get
				{
					lock (this)
					{
						return _log.Logger;
					}
				}
			}

			#endregion // PUBLIC PROPERTIES

			#region PUBLIC METHODS

			/// <summary>
			/// Writes the specified message to the log with Debug level.
			/// </summary>
			/// <param name="message">The message.</param>
			/// <param name="exception">The exception.</param>
			public void Debug(object message, Exception exception)
			{
				string text = GetText(message.ToString());

				_log.Debug(text, exception);
			}

			/// <summary>
			/// Writes the specified message to the log with Debug level.
			/// Method automatically indent th text.
			/// </summary>
			/// <param name="message">The message.</param>
			public void Debug(object message)
			{
				string text = GetText(message.ToString());

				_log.Debug(text);
			}

			/// <summary>
			/// Writes the specified message to the log with Debug level.
			/// Method automatically indent th text.
			/// </summary>
			/// <param name="message">The message.</param>
			public void DebugBegin(object message)
			{
				string text = GetBeginText(message.ToString());

				_log.Debug(text);
			}

			/// <summary>
			/// Writes the specified message to the log with Debug level.
			/// Method automatically indent th text.
			/// </summary>
			/// <param name="message">The message.</param>
			public void DebugEnd(object message)
			{
				string text = GetEndText(message.ToString());

				_log.Debug(text);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Debug level.
			/// </summary>
			/// <param name="provider">The provider.</param>
			/// <param name="format">The format.</param>
			/// <param name="args">The args collection.</param>
			public void DebugFormat(IFormatProvider provider, string format, params object[] args)
			{
				_log.DebugFormat(provider, format, args);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Debug level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			/// <param name="arg2">The arg2.</param>
			public void DebugFormat(string format, object arg0, object arg1, object arg2)
			{
				string text = GetText(format, arg0, arg1, arg2);

				_log.Debug(text);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Debug level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			public void DebugFormat(string format, object arg0, object arg1)
			{
				string text = GetText(format, arg0, arg1);

				_log.Debug(text);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Debug level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			public void DebugFormat(string format, object arg0)
			{
				string text = GetText(format, arg0);

				_log.Debug(text);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Debug level.
			/// Method automatically indent th text.
			/// </summary>
			/// <param name="format">The format.</param>
			/// <param name="args">The args collection.</param>
			public void DebugFormat(string format, params object[] args)
			{
				string text = GetText(format, args);

				_log.Debug(text);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Debug level.
			/// Method automatically indent th text.
			/// </summary>
			/// <param name="format">The format.</param>
			/// <param name="args">The args collection.</param>
			public void DebugBeginFormat(string format, params object[] args)
			{
				string text = GetBeginText(format, args);

				_log.Debug(text);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Debug level.
			/// Method automatically indent th text.
			/// </summary>
			/// <param name="format">The format.</param>
			/// <param name="args">The args collection.</param>
			public void DebugEndFormat(string format, params object[] args)
			{
				string text = GetEndText(format, args);

				_log.Debug(text);
			}

			/// <summary>
			/// Writes the specified message to the log with Error level.
			/// </summary>
			/// <param name="message">The message.</param>
			/// <param name="exception">The exception.</param>
			public void Error(object message, Exception exception)
			{
				_log.Error(message, exception);
			}

			/// <summary>
			/// Writes the specified message to the log with Error level.
			/// </summary>
			/// <param name="message">The message.</param>
			public void Error(object message)
			{
				_log.Error(message);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Error level.
			/// </summary>
			/// <param name="provider">The format provider.</param>
			/// <param name="format">The format definiton.</param>
			/// <param name="args">The args collection.</param>
			public void ErrorFormat(IFormatProvider provider, string format, params object[] args)
			{
				_log.ErrorFormat(provider, format, args);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Error level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			/// <param name="arg2">The arg2.</param>
			public void ErrorFormat(string format, object arg0, object arg1, object arg2)
			{
				_log.ErrorFormat(format, arg0, arg1, arg2);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Error level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			public void ErrorFormat(string format, object arg0, object arg1)
			{
				_log.ErrorFormat(format, arg0, arg1);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Error level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			public void ErrorFormat(string format, object arg0)
			{
				_log.ErrorFormat(format, arg0);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Error level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="args">The args collection.</param>
			public void ErrorFormat(string format, params object[] args)
			{
				_log.ErrorFormat(format, args);
			}

			/// <summary>
			/// Writes the specified message to the log with Fatal level.
			/// </summary>
			/// <param name="message">The message.</param>
			/// <param name="exception">The exception.</param>
			public void Fatal(object message, Exception exception)
			{
				_log.Fatal(message, exception);
			}

			/// <summary>
			/// Writes the specified message to the log with Fatal level.
			/// </summary>
			/// <param name="message">The message.</param>
			public void Fatal(object message)
			{
				_log.Fatal(message);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Fatal level.
			/// </summary>
			/// <param name="provider">The format provider.</param>
			/// <param name="format">The format definition.</param>
			/// <param name="args">The args collection.</param>
			public void FatalFormat(IFormatProvider provider, string format, params object[] args)
			{
				_log.FatalFormat(provider, format, args);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Fatal level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			/// <param name="arg2">The arg2.</param>
			public void FatalFormat(string format, object arg0, object arg1, object arg2)
			{
				_log.FatalFormat(format, arg0, arg1, arg2);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Fatal level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			public void FatalFormat(string format, object arg0, object arg1)
			{
				_log.FatalFormat(format, arg0, arg1);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Fatal level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			public void FatalFormat(string format, object arg0)
			{
				_log.FatalFormat(format, arg0);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Fatal level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="args">The args collection.</param>
			public void FatalFormat(string format, params object[] args)
			{
				_log.FatalFormat(format, args);
			}

			/// <summary>
			/// Writes the specified message to the log with Info level.
			/// </summary>
			/// <param name="message">The message.</param>
			/// <param name="exception">The exception.</param>
			public void Info(object message, Exception exception)
			{
				_log.Info(message, exception);
			}

			/// <summary>
			/// Writes the specified message to the log with Info level.
			/// </summary>
			/// <param name="message">The message.</param>
			public void Info(object message)
			{
				_log.Info(message);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Info level.
			/// </summary>
			/// <param name="provider">The format provider.</param>
			/// <param name="format">The format definition.</param>
			/// <param name="args">The args collection.</param>
			public void InfoFormat(IFormatProvider provider, string format, params object[] args)
			{
				_log.InfoFormat(provider, format, args);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Info level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			/// <param name="arg2">The arg2.</param>
			public void InfoFormat(string format, object arg0, object arg1, object arg2)
			{
				_log.InfoFormat(format, arg0, arg1, arg2);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Info level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			public void InfoFormat(string format, object arg0, object arg1)
			{
				_log.InfoFormat(format, arg0, arg1);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Info level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			public void InfoFormat(string format, object arg0)
			{
				_log.InfoFormat(format, arg0);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Info level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="args">The args collection.</param>
			public void InfoFormat(string format, params object[] args)
			{
				_log.InfoFormat(format, args);
			}

			/// <summary>
			/// Writes the specified message to the log with Warn level.
			/// </summary>
			/// <param name="message">The message.</param>
			/// <param name="exception">The exception.</param>
			public void Warn(object message, Exception exception)
			{
				_log.Warn(message, exception);
			}

			/// <summary>
			/// Writes the specified message to the log with Warn level.
			/// </summary>
			/// <param name="message">The message.</param>
			public void Warn(object message)
			{
				_log.Warn(message);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Warn level.
			/// </summary>
			/// <param name="provider">The format provider.</param>
			/// <param name="format">The format definition.</param>
			/// <param name="args">The args collection.</param>
			public void WarnFormat(IFormatProvider provider, string format, params object[] args)
			{
				_log.WarnFormat(provider, format, args);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Warn level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			/// <param name="arg2">The arg2.</param>
			public void WarnFormat(string format, object arg0, object arg1, object arg2)
			{
				_log.WarnFormat(format, arg0, arg1, arg2);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Warn level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			/// <param name="arg1">The arg1.</param>
			public void WarnFormat(string format, object arg0, object arg1)
			{
				_log.WarnFormat(format, arg0, arg1);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Warn level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="arg0">The arg0.</param>
			public void WarnFormat(string format, object arg0)
			{
				_log.WarnFormat(format, arg0);
			}

			/// <summary>
			/// Writes the specified formated message to the log with Warn level.
			/// </summary>
			/// <param name="format">The format definition.</param>
			/// <param name="args">The args collection.</param>
			public void WarnFormat(string format, params object[] args)
			{
				_log.WarnFormat(format, args);
			}

			#endregion // PUBLIC METHODS

			#region HELPER METHODS

			/// <summary>
			/// Gets the begin text.
			/// </summary>
			/// <param name="format">The format.</param>
			/// <param name="args">The args.</param>
			/// <returns></returns>
			private string GetBeginText(string format, params object[] args)
			{
				string spaces = GetSpaces(_deepIndex);
				string text = string.Format(format, args);

				_deepIndex++;

				return string.Format("{0}{1}", spaces, text);
			}

			/// <summary>
			/// Gets the begin text.
			/// </summary>
			/// <param name="text">The text.</param>
			/// <returns></returns>
			private string GetBeginText(string text)
			{
				string spaces = GetSpaces(_deepIndex);

				_deepIndex++;

				return string.Format("{0}{1}", spaces, text);
			}

			/// <summary>
			/// Gets the end text.
			/// </summary>
			/// <param name="format">The format.</param>
			/// <param name="args">The args.</param>
			/// <returns></returns>
			private string GetEndText(string format, params object[] args)
			{
				_deepIndex--;

				if (_deepIndex < 0)
				{
					_deepIndex = 0;
				}

				string spaces = GetSpaces(_deepIndex);
				string text = string.Format(format, args);

				return string.Format("{0}{1}", spaces, text);
			}

			/// <summary>
			/// Gets the end text.
			/// </summary>
			/// <param name="text">The text.</param>
			/// <returns></returns>
			private string GetEndText(string text)
			{
				_deepIndex--;

				if (_deepIndex < 0)
				{
					_deepIndex = 0;
				}

				string spaces = GetSpaces(_deepIndex);

				return string.Format("{0}{1}", spaces, text);
			}

			/// <summary>
			/// Gets the text.
			/// </summary>
			/// <param name="format">The format.</param>
			/// <param name="args">The args.</param>
			/// <returns></returns>
			private string GetText(string format, params object[] args)
			{
				string spaces = GetSpaces(_deepIndex);
				string text = string.Format(format, args);

				return string.Format("{0}{1}", spaces, text);
			}

			/// <summary>
			/// Gets the text.
			/// </summary>
			/// <param name="text">The text.</param>
			/// <returns></returns>
			private string GetText(string text)
			{
				string spaces = GetSpaces(_deepIndex);

				return string.Format("{0}{1}", spaces, text);
			}

			/// <summary>
			/// Gets the string spaces according specified depth.
			/// </summary>
			/// <param name="depth">The depth.</param>
			/// <returns></returns>
			private string GetSpaces(int depth)
			{
				StringBuilder sb = new StringBuilder();

				for (int i = 1; i <= depth; i++)
				{
					sb.Append(_deepSpace);
				}

				return sb.ToString();
			}

			#endregion // HELPER METHODS

		} // class LoggerWrapper

		#endregion // INNER CLASSES

		#region PRIVATE FIELDS

		private static Dictionary<Type, ILog> _loggers;

		#endregion // PRIVATE FIELDS

		#region PUBLIC STATIC METHODS

		public static void Initialize ()
		{
			LogManager.ResetConfiguration();
			_loggers = null;
		}

		/// <summary>
		/// Creates an instance of the logger class, and configures it from the 
		/// configuration file specified in the application configuration 
		/// settings, with key "LogConfigurationFile".
		/// </summary>
		/// <param name="type">
		/// The type of the calling class.
		/// </param>
		/// <returns></returns>
		public static ILog GetLogger (Type type)
		{
            ILog result = null;
			
            if (type == null)
            {
                throw new ApplicationException("Type cannot be null when initializing a logger");
            }

            if (_loggers == null)
            {
                _loggers = new Dictionary <Type,ILog>();
            }

            if (!(_loggers.TryGetValue (type, out result)))
            {
                result = LogManager.GetLogger (type);

				try
				{
					ConfigureAndWatch(); 
				}
				catch 
				{

				}

                _loggers [type] = result;
            }

			return result;
		}

		/// <summary>
		/// Gets the logger wrapper to avoid log4net framework dependency.
		/// </summary>
		/// <param name="type">The type.</param>
		/// <returns></returns>
		public static LoggerWrapper GetLoggerWrapper(Type type)
		{
			LoggerWrapper result = null;
			ILog logger;

			logger = Logging.GetLogger(type);

			result = new LoggerWrapper(logger);

			return (result);
		} // LoggerWrapper GetLogger(Type type)

		/// <summary>
		/// Calling this method will safely close and remove all appenders in 
		/// all the loggers including root contained in all the default repositories. 
		/// Some appenders need to be closed before the application exists. 
		/// Otherwise, pending logging events might be lost. 
		/// The shutdown method is careful to close nested appenders before closing regular appenders. 
		/// This is allows configurations where a regular appender is attached to a logger and 
		/// again to a nested appender. 
		/// </summary>
		public static void Shutdown()
		{
			// Safely closing of all appenders and clean up repository
			LogManager.Shutdown();
		}

		#endregion // PUBLIC STATIC METHODS

		#region PRIVATE METHODS

		/// <summary>
		/// Inits the logging system from configuration with watching.
		/// </summary>
		private static void ConfigureAndWatch()
		{
			Stream stream = null;

			try
			{
				stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Wanet.PublicPortal.Common.Config.log4netConfig.xml");

				if (stream != null)
				{
					XmlConfigurator.Configure(stream);
				}
				
			}
			catch (Exception ex)
			{
				throw new Exception("Cannot create a configuration for logging.", ex);
			}

		} // InitRepository

		#endregion // PRIVATE METHODS

	} // class Logging

} // namespace TietoEnator.PN.CSP.Utilities
